<?php
if( ! defined('BASEPATH')) {
    exit('No direct script access allowed');
}

/**
 * This script scan application folder, read all class 
 * constants with prefix 'NAME_' and 'DESC_' and CATAGORY,
 * if constants related method exist and access level is
 * public, gen one meta entry (class, func, name, description, 
 * catagory_id), and insert/replace this entry into database.
 *
 * $Id: gen_app_meta.php 183 2012-02-25 07:16:27Z leikou@163.com $
 */


// Extends RecursiveFilterIterator
class GenFilter extends RecursiveFilterIterator
{
    private $valid_extensions;
    private $exclude_folders = array(
        '.svn'    
    );


    public function __construct($iterator)
    {
        parent::__construct($iterator);

        $this->valid_extensions = array('php');
    }


    public function accept()
    {
        $current = $this->getInnerIterator()->current();
        $file_name = $current->getFilename();

        if($current->isDir())
        {
            if(in_array($file_name, $this->exclude_folders))
            {
                return FALSE;
            }

            return TRUE;
        }

        if( ! $current->isFile())
        {
            return FALSE;
        }

        if(method_exists($current, 'getExtension'))
        {
            $extension = $current->getExtension();
        }
        else
        {
            $seg_array = explode('.', $file_name);
            $extension = array_pop($seg_array);
        }

        return in_array($extension, $this->valid_extensions);
    }


    public function getChildren()
    {
        return new self(
            $this->getInnerIterator()->getChildren(),
            $this->valid_extensions
        );
    }
}



// Extends RecursiveDirectory
class GenMetaRecursive extends RecursiveDirectoryIterator
{
    const META_NAME = 'NAME_';
    const META_DESC = 'DESC_';
    const META_METHOD = 'METHOD_';
    const META_CATAGORY = 'CATAGORY';
    const FILE_SUFFIX = '.php';
    

    public function __construct($path)
    {
        parent::__construct($path,  FilesystemIterator::CURRENT_AS_SELF);
    }


    // Get class, func, name, desc and catagory
    public function get_meta()
    {
        $class_name = $this->_get_class_name();
        if($class_name === NULL)
        {
            return array();
        }

        $meta_array = array();

        $reflection = new ReflectionClass($class_name);
        $public_methods = $reflection->getMethods(
            ReflectionMethod::IS_PUBLIC
        );

        // Get catagory info
        $catagory = $reflection->getConstant(self::META_CATAGORY);
        if($catagory === FALSE) {
            return array();
        }

        $docblock =& load_class('Docblock', 'libraries', '');
        foreach($public_methods as $method)
        {
            $doc_comment = $method->getDocComment();
            list($comments, $tags) = $docblock::parse($doc_comment);
            $cf_chinese_name = isset($tags['name']) ? $tags['name'] : NULL;
            $cf_description = isset($tags['desc']) ? $tags['desc'] : NULL;
            $cf_method = isset($tags['method']) ? strtoupper(trim($tags['method'])) : NULL;

            if($cf_chinese_name === NULL || $cf_description === NULL)
            {
                continue;
            }

            if($cf_method == 'POST')
            {
                $cf_method = OA_CONST_CLASS_FUNC::METHOD_POST;
            }
            else if($cf_method == 'GET')
            {
                $cf_method = OA_CONST_CLASS_FUNC::METHOD_GET;
            }
            else
            {
                $cf_method = OA_CONST_CLASS_FUNC::METHOD_BOTH;
            }

            $meta_array[] = array(
                'class' => $class_name, 
                'func' => $method->getName(), 
                'name' => $cf_chinese_name, 
                'description' => $cf_description,
                'catagory_id' => $catagory,
                'method' => $cf_method,
                'status' => OA_CONST_CLASS_FUNC::STATUS_NORMAL,
            );
        }

        return $meta_array;
    }


    // Get class name
    private function _get_class_name() {
        $current = $this->current();
        if( ! $current->isFile()) {
            return NULL;
        }

        $class_name = $current->getBasename(self::FILE_SUFFIX);
        $absolute_file = $current->getRealPath();

        require_once $absolute_file;

        if(class_exists($class_name))
        {
            return $class_name;
        }
        else
        {
            return NULL;
        }
    }
}



// Iterator app path, and update meta info into database
class Gen_app_meta extends CI_Controller
{
    const RECURSIVE_MAX_DEPATH = 0;


    public static function get_meta()
    {
        static $iterator = NULL;
        if($iterator === NULL)
        {
            $iterator = new RecursiveIteratorIterator(
                new GenFilter(
                    new GenMetaRecursive(REALAPPPATH.'controllers')
                )
            );
        }

        $iterator->setMaxDepth(self::RECURSIVE_MAX_DEPATH);

        $meta_array = array();
        foreach($iterator as $file_path => $current)
        {
            $meta_array = array_merge($meta_array, $current->get_meta());
        }

        return $meta_array;
    }

    public function gen()
    {
        $metas = $this->get_meta();

        // Update meta_data into database
        $this->load->database();

        $this->db->trans_start();

        // Update all entries status to delete. 
        $this->db
            ->update('class_func_description', array(
                'status' => OA_CONST_CLASS_FUNC::STATUS_DELETE,
            ));

        foreach($metas as $meta)
        {
            $this->db
                ->where('class', $meta['class'])
                ->where('func', $meta['func'])
                ->update('class_func_description', $meta);
            if($this->db->affected_rows() !== 1)
            {
                $this->db->insert('class_func_description', $meta);
            }
        }

        $this->db->trans_complete();
    }
}

/* End of file gen_app_meta.php */
/* Location: ./application/controllers/tools/gen_app_meta.php */
